import { Injectable, Logger } from '@nestjs/common';
import {
  QuerySettingInterface,
  SettingInterface,
} from './interface/setting.interface';
import { Common } from '../common/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ElectionSettings } from '../../module/ElectionSettings';
import { Repository, SelectQueryBuilder, UpdateResult } from 'typeorm';
import Redis from 'ioredis';
import { RedisService } from '@liaoliaots/nestjs-redis';
import { RedisConfig } from '../../@nt/config';
import { CrontabService } from '../crontab/crontab.service';
import * as moment from 'moment/moment';
import { VoteService } from '../vote/vote.service';
import { Candidates } from '../../module/Candidates';

@Injectable()
export class SettingService extends Common {
  private readonly logger = new Logger(SettingService.name);
  private readonly redis: Redis | null;

  constructor(
    @InjectRepository(ElectionSettings)
    private readonly settingRepository: Repository<ElectionSettings>,
    @InjectRepository(Candidates)
    private readonly candidateRepository: Repository<Candidates>,
    private readonly voteService: VoteService,
    private readonly redisService: RedisService,
    private readonly crontabService: CrontabService,
  ) {
    super();
    this.redis = this.redisService.getOrThrow();
  }

  /**
   * 添加投票时间设置
   * @param body
   */
  async create(body: SettingInterface): Promise<any> {
    const where = {
      startTime: body.startTime,
      endTime: body.endTime,
    };
    let setting: any = await this.settingRepository.findOne({
      where,
    });
    if (setting) {
      return this.returnJson(-1, setting, '已存在该时间段的设置');
    }
    const data: any = {
      ...body,
    };
    setting = await this.settingRepository.save(data);
    const setData: any = {
      isActive: setting.isActive,
      startTime: setting.startTime,
      endTime: setting.endTime,
      id: setting.id,
    };
    this.settingRedisInit(setData);
    if (setting) {
      return this.returnJson(0, setting, '成功');
    }
    return this.returnJson(-1, null, '失败');
  }

  /**
   * 根据ID获取投票时间设置
   * @param id
   */
  async findOne(id: number): Promise<any> {
    const where = { id };
    const setting: ElectionSettings = await this.settingRepository.findOne({
      where,
    });
    if (setting) {
      return this.returnJson(0, setting, '成功');
    }
    return this.returnJson(-1, null, '失败');
  }

  async findAll(query: QuerySettingInterface): Promise<any> {
    const where: any = {};
    if (query.isActive) {
      where.isActive = query.isActive;
    }
    const limit: number = query.pageSize || 100;
    const offset: number = (query.pageNo - 1) * limit;
    const repository: SelectQueryBuilder<ElectionSettings> =
      this.settingRepository.createQueryBuilder('setting');
    const totalCount: number = await repository.where(where).getCount();
    const result = await repository
      .select('setting.*')
      .where(where)
      .orderBy('id', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
    return this.returnJson(
      0,
      this.returnPage(query.pageNo, totalCount, result),
    );
  }

  /**
   * 更新投票时间设置
   * @param id
   * @param body
   */
  async update(id: number, body: SettingInterface): Promise<any> {
    const where = { id };
    const setting: ElectionSettings = await this.settingRepository.findOne({
      where,
    });
    if (setting) {
      if (setting.endTime < moment().unix()) {
        return this.returnJson(-1, false, '该选举场次已结束，不可修改');
      }
      const data: any = {};
      if (body.startTime) {
        data.startTime = body.startTime;
      }
      if (body.endTime) {
        data.endTime = body.endTime;
      }
      if (body.isActive) {
        data.isActive = body.isActive;
      }
      const result: any = await this.settingRepository.update(where, data);
      if (result.affected) {
        const startTime = data.startTime || setting.startTime;
        const endTime = data.endTime || setting.endTime;
        const setData: any = {
          isActive: data.isActive,
          startTime,
          endTime,
          id,
        };
        this.settingRedisInit(setData);
        return this.returnJson(0, result.affected, '成功');
      }
      return this.returnJson(-1, null, '更新失败');
    }
    return this.returnJson(-1, null, '更新失败');
  }

  /**
   * 结束选举
   * @param id
   */
  async endElection(id: number): Promise<any> {
    const where = { id };
    const setting: ElectionSettings = await this.settingRepository.findOne({
      where,
    });
    if (!setting) {
      return this.returnJson(-1, null, '未找到设置');
    }
    if (setting.endTime < moment().unix()) {
      return this.returnJson(-1, null, '当前时间已超过设置的结束时间');
    }
    if (!setting.isActive) {
      return this.returnJson(-1, null, '当前设置已结束');
    }
    const result: UpdateResult = await this.settingRepository.update(where, {
      isActive: false,
    });
    if (result.affected) {
      this.voteService.persistVotes(setting.id).then(() => {});
      return this.returnJson(0, result.affected, '成功');
    }
    return this.returnJson(-1, null, '失败');
  }

  /**
   * 根据选举场次ID获取投票结果
   * @param id
   */
  async getElectionResults(id: number): Promise<any> {
    const currentElection = await this.settingRepository.findOne({
      where: { id },
    });
    if (!currentElection) {
      return this.returnJson(-1, null, '未找到选举场次');
    }

    const now = moment().unix();
    const isElectionActive =
      now >= currentElection.startTime && now <= currentElection.endTime;

    let result: any = [];
    if (isElectionActive) {
      result = await this.getResultsFromRedis(currentElection.id);
    } else {
      result = await this.getResultsFromDatabase(currentElection.id);
    }
    return this.returnJson(0, result, '成功');
  }

  // 从Redis获取实时票数
  private async getResultsFromRedis(electionId: number): Promise<any> {
    const candidateVotes = await this.redis.hgetall(
      `${RedisConfig.electionCandidateVotes}${electionId}`,
    );

    const candidates = await this.candidateRepository.findByIds(
      Object.keys(candidateVotes).map(Number),
    );

    return candidates.map((candidate) => ({
      id: candidate.id,
      name: candidate.name,
      votes: parseInt(candidateVotes[candidate.id]) || 0,
    }));
  }

  // 从数据库获取持久化票数
  private async getResultsFromDatabase(electionId: number): Promise<any> {
    return await this.candidateRepository
      .createQueryBuilder('candidate')
      .leftJoinAndSelect(
        'candidate.votes',
        'vote',
        'vote.election_setting_id = :electionId',
        { electionId },
      )
      .select(['candidate.id', 'candidate.name', 'COUNT(vote.id) as votes'])
      .groupBy('candidate.id')
      .getRawMany();
  }

  settingRedisInit(data: {
    isActive: number;
    startTime: number;
    endTime: number;
    id: number;
  }) {
    if (data.isActive) {
      this.redis.hmset(
        RedisConfig.electionSetting,
        'start_time',
        data.startTime,
        'end_time',
        data.endTime,
      );
      this.redis.set(`${RedisConfig.currentElection}`, data.id);
      this.crontabService.scheduleJob(
        'election.end.job',
        moment.unix(data.endTime).toISOString(),
        data.id,
      );
    }
  }
}
